/*
 * espnow_radio.c
 *
 *  Created on: 2018-3-13
 *      Author: Iron
 */
#include "queue.h"
#include "mem.h"
#include "osapi.h"
#include "user_interface.h"
#include "contiki.h"
#include "espnow.h"
#include "espnow_radio.h"

/*---------------------------------------------------------------------------*/
void contiki_radio_tx_cb(void);
void contiki_radio_rx_cb(void);

/*---------------------------------------------------------------------------*/
#ifndef ESP_NOW_KEY_LEN
	#define ESP_NOW_KEY_LEN		16
#endif

#ifndef ESP_NOW_ETH_ALEN
	#define ESP_NOW_ETH_ALEN	6
#endif

static uint8_t espnow_broadcast_mac[ESP_NOW_ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };

/*---------------------------------------------------------------------------*/
#define ESPNOW_RADIO_RX_PAKAGE_NUM	10

struct _espnow_radio_rx_pakage_t{
	SLIST_ENTRY(_espnow_radio_rx_pakage_t) next;
	int data_len;
	void *data;
	uint8_t mac_addr[ESP_NOW_ETH_ALEN];
};

static SLIST_HEAD(_espnow_radio_rx_list_t , _espnow_radio_rx_pakage_t) \
	_espnow_radio_rx_list, _espnow_radio_rx_free_list, *espnow_radio_rx_list, *espnow_radio_rx_free_list;

static struct _espnow_radio_rx_pakage_t espnow_radio_rx_list_elms[ESPNOW_RADIO_RX_PAKAGE_NUM];

void ICACHE_FLASH_ATTR espnow_radio_rx_queue_init(void)
{
	int i;

	espnow_radio_rx_list = &_espnow_radio_rx_list;
	SLIST_INIT(espnow_radio_rx_list);

	espnow_radio_rx_free_list = &_espnow_radio_rx_free_list;
	SLIST_INIT(espnow_radio_rx_free_list);

    for (i = 0; i < ESPNOW_RADIO_RX_PAKAGE_NUM; i++) {
    	os_memset(&espnow_radio_rx_list_elms[i], 0, sizeof(struct _espnow_radio_rx_pakage_t));
    	SLIST_INSERT_HEAD(espnow_radio_rx_free_list, &espnow_radio_rx_list_elms[i], next);
    }
}

void ICACHE_FLASH_ATTR espnow_radio_rx_queue_in(u8 *mac_addr, u8 *data, u8 len)
{
	struct _espnow_radio_rx_pakage_t *rx_pakage;

	if(SLIST_EMPTY(espnow_radio_rx_free_list)){
		return;
	}

	rx_pakage = SLIST_FIRST(espnow_radio_rx_free_list);
	if(rx_pakage){
		rx_pakage->data = os_malloc(len);
		if(rx_pakage->data){
			rx_pakage->data_len = len;
			os_memcpy(rx_pakage->mac_addr, mac_addr, ESP_NOW_ETH_ALEN);
			os_memcpy(rx_pakage->data, data, len);
			SLIST_INSERT_HEAD(espnow_radio_rx_list, rx_pakage, next);
			SLIST_REMOVE_HEAD(espnow_radio_rx_free_list, next);
		}
	}
}

int ICACHE_FLASH_ATTR espnow_radio_rx_queue_out(void *buf, unsigned short buf_len)
{
	int len;
	uint8_t *mac_addr;
	struct _espnow_radio_rx_pakage_t *rx_pakage;

	rx_pakage = SLIST_FIRST(espnow_radio_rx_list);
	if(rx_pakage == NULL){
		return 0;
	}

	len = rx_pakage->data_len;
	os_memcpy(buf, rx_pakage->data, len);

	SLIST_INSERT_HEAD(espnow_radio_rx_free_list, rx_pakage, next);
	SLIST_REMOVE_HEAD(espnow_radio_rx_list, next);

	mac_addr = rx_pakage->mac_addr;
	os_printf("espnow rx data len=%d from mac: [%02x%02x%02x%02x%02x%02x]\n",\
			len, mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
	if(esp_now_is_peer_exist(mac_addr) == 0){
		// remove dead perr
		esp_now_add_peer(mac_addr, ESP_NOW_ROLE_COMBO, 0, NULL, 16);
	}

	os_free(rx_pakage->data);
	os_memset(rx_pakage, 0, sizeof(struct _espnow_radio_rx_pakage_t));

	return len;
}

/*---------------------------------------------------------------------------*/
LOCAL void ICACHE_FLASH_ATTR espnow_radio_send_cb(u8 *mac_addr, u8 status)
{
	contiki_radio_tx_cb();
//	espnow_radio_tx_flag = 1;
//	os_printf("espnow_radio_send_cb\n");
}

LOCAL void ICACHE_FLASH_ATTR espnow_radio_recv_cb(u8 *mac_addr, u8 *data, u8 len)
{
	espnow_radio_rx_queue_in(mac_addr, data, len);
	contiki_radio_rx_cb();
}

/*---------------------------------------------------------------------------*/
int ICACHE_FLASH_ATTR espnow_radio_init(void)
{
	int rc;

	espnow_radio_rx_queue_init();

	/* Initialize ESPNOW and register sending and receiving callback function. */
	if(esp_now_init() != 0) {
		os_printf("esp_now init failed\n");
		return -1;
	}

	esp_now_register_send_cb(espnow_radio_send_cb);
	esp_now_register_recv_cb(espnow_radio_recv_cb);
	esp_now_set_self_role(ESP_NOW_ROLE_COMBO);

	return 0;
}

int ICACHE_FLASH_ATTR espnow_radio_send(u8 *data, int len)
{
	os_printf("espnow_radio_send len=%d\n", len);

	esp_now_send(espnow_broadcast_mac, data, len);
	esp_now_send(NULL, data, len);

	return 0;
}

/*---------------------------------------------------------------------------*/
static int init(void)
{
	espnow_radio_init();
	return 0;
}

/*---------------------------------------------------------------------------*/
static int prepare(const void *payload, unsigned short payload_len)
{
	return 1;
}

/*---------------------------------------------------------------------------*/
static int transmit(unsigned short transmit_len)
{
	return RADIO_TX_OK;
}

/*---------------------------------------------------------------------------*/
static int send(const void *payload, unsigned short payload_len)
{
	prepare(payload, payload_len);
	espnow_radio_send((u8*)payload, payload_len);
	return transmit(payload_len);
}

/*---------------------------------------------------------------------------*/
static int radio_read(void *buf, unsigned short buf_len)
{
	int len = 0;

	len = espnow_radio_rx_queue_out(buf, buf_len);

	return len;
}

/*---------------------------------------------------------------------------*/
static int channel_clear(void)
{
	return 1;
}

/*---------------------------------------------------------------------------*/
static int receiving_packet(void)
{
	return 0;
}

/*---------------------------------------------------------------------------*/
static int pending_packet(void)
{
	return 0;
}

/*---------------------------------------------------------------------------*/
static int on(void)
{
	return 0;
}

/*---------------------------------------------------------------------------*/
static int off(void)
{
	return 0;
}

/*---------------------------------------------------------------------------*/
static radio_result_t get_value(radio_param_t param, radio_value_t *value)
{
	return RADIO_RESULT_NOT_SUPPORTED;
}

/*---------------------------------------------------------------------------*/
static radio_result_t set_value(radio_param_t param, radio_value_t value)
{
	return RADIO_RESULT_NOT_SUPPORTED;
}

/*---------------------------------------------------------------------------*/
static radio_result_t get_object(radio_param_t param, void *dest, size_t size)
{
	return RADIO_RESULT_NOT_SUPPORTED;
}

/*---------------------------------------------------------------------------*/
static radio_result_t set_object(radio_param_t param, const void *src, size_t size)
{
	return RADIO_RESULT_NOT_SUPPORTED;
}

/*---------------------------------------------------------------------------*/
const struct radio_driver espnow_radio_driver =
  {
    init,
    prepare,
    transmit,
    send,
  	radio_read,
    channel_clear,
  	receiving_packet,
    pending_packet,
    on,
    off,
    get_value,
    set_value,
    get_object,
    set_object
  };
/*---------------------------------------------------------------------------*/




